1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  import static org.easymock.EasyMock.anyObject;
19  import static org.easymock.EasyMock.createMock;
20  import static org.easymock.EasyMock.expect;
21  import static org.easymock.EasyMock.expectLastCall;
22  import static org.easymock.EasyMock.replay;
23  import static org.easymock.EasyMock.verify;
24  
25  import com.google.common.collect.testing.MapTestSuiteBuilder;
26  import com.google.common.collect.testing.TestStringMapGenerator;
27  import com.google.common.collect.testing.features.CollectionFeature;
28  import com.google.common.collect.testing.features.CollectionSize;
29  import com.google.common.collect.testing.features.MapFeature;
30  
31  import junit.framework.Test;
32  import junit.framework.TestSuite;
33  
34  import java.lang.reflect.InvocationTargetException;
35  import java.util.Collection;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.Map.Entry;
40  import java.util.Set;
41  
42  /**
43   * Unit test for {@link ForwardingMap}.
44   *
45   * @author Hayward Chan
46   * @author Louis Wasserman
47   */
48  public class ForwardingMapTest extends ForwardingTestCase {
49    static class StandardImplForwardingMap<K, V> extends ForwardingMap<K, V> {
50      private final Map<K, V> backingMap;
51  
52      StandardImplForwardingMap(Map<K, V> backingMap) {
53        this.backingMap = backingMap;
54      }
55  
56      @Override protected Map<K, V> delegate() {
57        return backingMap;
58      }
59  
60      @Override public boolean containsKey(Object key) {
61        return standardContainsKey(key);
62      }
63  
64      @Override public boolean containsValue(Object value) {
65        return standardContainsValue(value);
66      }
67  
68      @Override public void putAll(Map<? extends K, ? extends V> map) {
69        standardPutAll(map);
70      }
71  
72      @Override public V remove(Object object) {
73        return standardRemove(object);
74      }
75  
76      @Override public boolean equals(Object object) {
77        return standardEquals(object);
78      }
79  
80      @Override public int hashCode() {
81        return standardHashCode();
82      }
83  
84      @Override public Set<K> keySet() {
85        return new StandardKeySet();
86      }
87  
88      @Override public Collection<V> values() {
89        return new StandardValues();
90      }
91  
92      @Override public String toString() {
93        return standardToString();
94      }
95  
96      @Override public Set<Entry<K, V>> entrySet() {
97        return new StandardEntrySet() {
98          @Override
99          public Iterator<Entry<K, V>> iterator() {
100           return delegate()
101               .entrySet()
102               .iterator();
103         }
104       };
105     }
106 
107     @Override public void clear() {
108       standardClear();
109     }
110 
111     @Override public boolean isEmpty() {
112       return standardIsEmpty();
113     }
114   }
115 
116   Map<String, Boolean> forward;
117 
118   public static Test suite() {
119     TestSuite suite = new TestSuite();
120 
121     suite.addTestSuite(ForwardingMapTest.class);
122     suite.addTest(MapTestSuiteBuilder.using(new TestStringMapGenerator() {
123 
124       @Override protected Map<String, String> create(
125           Entry<String, String>[] entries) {
126         Map<String, String> map = Maps.newLinkedHashMap();
127         for (Entry<String, String> entry : entries) {
128           map.put(entry.getKey(), entry.getValue());
129         }
130         return new StandardImplForwardingMap<String, String>(map);
131       }
132 
133     }).named("ForwardingMap[LinkedHashMap] with standard implementations")
134         .withFeatures(CollectionSize.ANY, MapFeature.ALLOWS_NULL_VALUES,
135             MapFeature.ALLOWS_NULL_KEYS, MapFeature.ALLOWS_ANY_NULL_QUERIES,
136             MapFeature.GENERAL_PURPOSE,
137             CollectionFeature.SUPPORTS_ITERATOR_REMOVE, CollectionFeature.KNOWN_ORDER)
138         .createTestSuite());
139     suite.addTest(MapTestSuiteBuilder.using(new TestStringMapGenerator() {
140 
141       @Override protected Map<String, String> create(
142           Entry<String, String>[] entries) {
143         ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
144         for (Entry<String, String> entry : entries) {
145           builder.put(entry.getKey(), entry.getValue());
146         }
147         return new StandardImplForwardingMap<String, String>(builder.build());
148       }
149 
150     }).named("ForwardingMap[ImmutableMap] with standard implementations")
151         .withFeatures(
152             CollectionSize.ANY, MapFeature.REJECTS_DUPLICATES_AT_CREATION,
153             MapFeature.ALLOWS_ANY_NULL_QUERIES,
154             CollectionFeature.KNOWN_ORDER)
155         .createTestSuite());
156 
157     return suite;
158   }
159 
160   @Override public void setUp() throws Exception {
161     super.setUp();
162     /*
163      * Class parameters must be raw, so we can't create a proxy with generic
164      * type arguments. The created proxy only records calls and returns null, so
165      * the type is irrelevant at runtime.
166      */
167     @SuppressWarnings("unchecked")
168     final Map<String, Boolean> map = createProxyInstance(Map.class);
169     forward = new ForwardingMap<String, Boolean>() {
170       @Override protected Map<String, Boolean> delegate() {
171         return map;
172       }
173     };
174   }
175 
176   public void testSize() {
177     forward().size();
178     assertEquals("[size]", getCalls());
179   }
180 
181   public void testIsEmpty() {
182     forward().isEmpty();
183     assertEquals("[isEmpty]", getCalls());
184   }
185 
186   public void testRemove() {
187     forward().remove(null);
188     assertEquals("[remove(Object)]", getCalls());
189   }
190 
191   public void testClear() {
192     forward().clear();
193     assertEquals("[clear]", getCalls());
194   }
195 
196   public void testContainsKey() {
197     forward().containsKey("asdf");
198     assertEquals("[containsKey(Object)]", getCalls());
199   }
200 
201   public void testContainsValue() {
202     forward().containsValue(false);
203     assertEquals("[containsValue(Object)]", getCalls());
204   }
205 
206   public void testGet_Object() {
207     forward().get("asdf");
208     assertEquals("[get(Object)]", getCalls());
209   }
210 
211   public void testPut_Key_Value() {
212     forward().put("key", false);
213     assertEquals("[put(Object,Object)]", getCalls());
214   }
215 
216   public void testPutAll_Map() {
217     forward().putAll(new HashMap<String, Boolean>());
218     assertEquals("[putAll(Map)]", getCalls());
219   }
220 
221   public void testKeySet() {
222     forward().keySet();
223     assertEquals("[keySet]", getCalls());
224   }
225 
226   public void testValues() {
227     forward().values();
228     assertEquals("[values]", getCalls());
229   }
230 
231   public void testEntrySet() {
232     forward().entrySet();
233     assertEquals("[entrySet]", getCalls());
234   }
235 
236   public void testToString() {
237     forward().toString();
238     assertEquals("[toString]", getCalls());
239   }
240 
241   public void testEquals_Object() {
242     forward().equals("asdf");
243     assertEquals("[equals(Object)]", getCalls());
244   }
245 
246   public void testHashCode() {
247     forward().hashCode();
248     assertEquals("[hashCode]", getCalls());
249   }
250 
251   public void testStandardEntrySet() throws InvocationTargetException {
252     @SuppressWarnings("unchecked")
253     final Map<String, Boolean> map = createMock(Map.class);
254     @SuppressWarnings("unchecked")
255     final Set<Map.Entry<String, Boolean>> entrySet = createMock(Set.class);
256     expect(map.containsKey(anyObject())).andReturn(false).anyTimes();
257     expect(map.get(anyObject())).andReturn(null).anyTimes();
258     expect(map.isEmpty()).andReturn(true).anyTimes();
259     expect(map.remove(anyObject())).andReturn(null).anyTimes();
260     expect(map.size()).andReturn(0).anyTimes();
261     expect(entrySet.iterator())
262         .andReturn(Iterators.<Entry<String, Boolean>>emptyIterator())
263         .anyTimes();
264     map.clear();
265     expectLastCall().anyTimes();
266 
267     replay(map, entrySet);
268 
269     Map<String, Boolean> forward = new ForwardingMap<String, Boolean>() {
270       @Override protected Map<String, Boolean> delegate() {
271         return map;
272       }
273 
274       @Override public Set<Entry<String, Boolean>> entrySet() {
275         return new StandardEntrySet() {
276           @Override
277           public Iterator<Entry<String, Boolean>> iterator() {
278             return entrySet.iterator();
279           }
280         };
281       }
282     };
283     callAllPublicMethods(Set.class, forward.entrySet());
284 
285     verify(map, entrySet);
286   }
287 
288   public void testStandardKeySet() throws InvocationTargetException {
289     @SuppressWarnings("unchecked")
290     Set<Entry<String, Boolean>> entrySet = createMock(Set.class);
291     expect(entrySet.iterator()).andReturn(
292         Iterators.<Entry<String, Boolean>>emptyIterator()).anyTimes();
293 
294     @SuppressWarnings("unchecked")
295     final Map<String, Boolean> map = createMock(Map.class);
296     expect(map.containsKey(anyObject())).andReturn(false).anyTimes();
297     expect(map.isEmpty()).andReturn(true).anyTimes();
298     expect(map.remove(anyObject())).andReturn(null).anyTimes();
299     expect(map.size()).andReturn(0).anyTimes();
300     expect(map.entrySet()).andReturn(entrySet).anyTimes();
301     map.clear();
302     expectLastCall().anyTimes();
303 
304     replay(entrySet, map);
305 
306     Map<String, Boolean> forward = new ForwardingMap<String, Boolean>() {
307       @Override protected Map<String, Boolean> delegate() {
308         return map;
309       }
310 
311       @Override public Set<String> keySet() {
312         return new StandardKeySet();
313       }
314     };
315     callAllPublicMethods(Set.class, forward.keySet());
316 
317     verify(entrySet, map);
318   }
319 
320   public void testStandardValues() throws InvocationTargetException {
321     @SuppressWarnings("unchecked")
322     Set<Entry<String, Boolean>> entrySet = createMock(Set.class);
323     expect(entrySet.iterator()).andReturn(
324         Iterators.<Entry<String, Boolean>>emptyIterator()).anyTimes();
325 
326     @SuppressWarnings("unchecked")
327     final Map<String, Boolean> map = createMock(Map.class);
328     expect(map.containsValue(anyObject())).andReturn(false).anyTimes();
329     expect(map.isEmpty()).andReturn(true).anyTimes();
330     expect(map.size()).andReturn(0).anyTimes();
331     expect(map.entrySet()).andReturn(entrySet).anyTimes();
332     map.clear();
333     expectLastCall().anyTimes();
334 
335     replay(entrySet, map);
336 
337     Map<String, Boolean> forward = new ForwardingMap<String, Boolean>() {
338       @Override protected Map<String, Boolean> delegate() {
339         return map;
340       }
341 
342       @Override public Collection<Boolean> values() {
343         return new StandardValues();
344       }
345     };
346     callAllPublicMethods(Collection.class, forward.values());
347 
348     verify(entrySet, map);
349   }
350 
351   public void testToStringWithNullKeys() throws Exception {
352     Map<String, String> hashmap = Maps.newHashMap();
353     hashmap.put("foo", "bar");
354     hashmap.put(null, "baz");
355 
356     StandardImplForwardingMap<String, String> forwardingMap =
357         new StandardImplForwardingMap<String, String>(
358             Maps.<String, String>newHashMap());
359     forwardingMap.put("foo", "bar");
360     forwardingMap.put(null, "baz");
361 
362     assertEquals(hashmap.toString(), forwardingMap.toString());
363   }
364 
365   public void testToStringWithNullValues() throws Exception {
366     Map<String, String> hashmap = Maps.newHashMap();
367     hashmap.put("foo", "bar");
368     hashmap.put("baz", null);
369 
370     StandardImplForwardingMap<String, String> forwardingMap =
371         new StandardImplForwardingMap<String, String>(
372             Maps.<String, String>newHashMap());
373     forwardingMap.put("foo", "bar");
374     forwardingMap.put("baz", null);
375 
376     assertEquals(hashmap.toString(), forwardingMap.toString());
377   }
378 
379   Map<String, Boolean> forward() {
380     return forward;
381   }
382 }